package com.ming.mallchat.common.user.service.impl;

import com.ming.mallchat.common.common.annotation.RedissonLock;
import com.ming.mallchat.common.common.domain.vo.resp.ApiResult;
import com.ming.mallchat.common.common.event.UserBlackEvent;
import com.ming.mallchat.common.common.event.UserRegisterEvent;
import com.ming.mallchat.common.common.utils.AssertUtil;
import com.ming.mallchat.common.common.utils.JsonUtils;
import com.ming.mallchat.common.user.cache.ItemCache;
import com.ming.mallchat.common.user.cache.UserCache;
import com.ming.mallchat.common.user.cache.UserSummaryCache;
import com.ming.mallchat.common.user.dao.BlackDao;
import com.ming.mallchat.common.user.dao.ItemConfigDao;
import com.ming.mallchat.common.user.dao.UserBackPackDao;
import com.ming.mallchat.common.user.dao.UserDao;
import com.ming.mallchat.common.user.domain.dto.ItemInfoDTO;
import com.ming.mallchat.common.user.domain.dto.SummeryInfoDTO;
import com.ming.mallchat.common.user.domain.enums.BlackTypeEnum;
import com.ming.mallchat.common.user.domain.enums.ItemEnum;
import com.ming.mallchat.common.user.domain.enums.ItemTypeEnum;
import com.ming.mallchat.common.user.domain.pojos.IpInfo;
import com.ming.mallchat.common.user.domain.vo.req.user.ItemInfoReq;
import com.ming.mallchat.common.user.domain.vo.req.user.SummeryInfoReq;
import com.ming.mallchat.common.user.domain.vo.resp.user.BadgeResp;
import com.ming.mallchat.common.user.pojo.Black;
import com.ming.mallchat.common.user.pojo.ItemConfig;
import com.ming.mallchat.common.user.pojo.User;
import com.ming.mallchat.common.user.pojo.UserBackpack;
import com.ming.mallchat.common.user.service.UserService;
import com.ming.mallchat.common.user.service.adpater.UserAdapter;
import com.ming.mallchat.common.user.domain.vo.resp.user.UserInfoResp;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 86150
 * @description 针对表【user(用户表)】的数据库操作Service实现
 * @createDate 2023-09-26 14:58:20
 */
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Resource
    private UserBackPackDao userBackPackDao;

    @Autowired
    private ItemConfigDao itemConfigDao;

    @Autowired
    private ItemCache itemCache;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private BlackDao blackDao;

    @Autowired
    private UserSummaryCache userSummaryCache;

    @Autowired
    private UserCache userCache;

    @Override
    @Transactional
    public Long register(User user) {
        userDao.save(user);
        //用户注册事件
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
        return user.getId();
    }

    /**
     * 获取用户基本信息
     *
     * @param uid 用户id
     * @return {@link ApiResult}<{@link UserInfoResp}>
     */
    @Override
    public UserInfoResp userinfo(Long uid) {
        User user = userDao.getById(uid);
        Integer count = userBackPackDao.getCountValidItemId(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        return UserAdapter.buildUserInfo(user, count);
    }

    /**
     * @param name 用户修改的名称
     * @param uid  用户id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedissonLock(key = "#uid", waitTime = 5000)
    public void modifyName(String name, Long uid) {
        User oldUser = userDao.getByName(name);
        AssertUtil.isEmpty(oldUser, "名字被抢占了，请换一个名字在试吧");
        UserBackpack modifyNameItem = userBackPackDao.getFirstValidItem(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        AssertUtil.isNotEmpty(modifyNameItem, "改名卡不够了，等后续活动送改名卡吧");
        Boolean success = userBackPackDao.userItem(modifyNameItem);
        if (success) {
            userDao.modifyName(name, uid);
            userCache.userInfoChange(uid);
        }
    }

    /**
     * @param uid 用户id
     * @return {@link List}<{@link BadgeResp}>
     */
    @Override
    public List<BadgeResp> badges(Long uid) {
        //获取全部徽章列表
        List<ItemConfig> itemConfig = itemCache.getByType(ItemTypeEnum.BADGE.getType());
        //获取用户拥有的徽章
        List<UserBackpack> userBackpacks = userBackPackDao.getByItemIds(uid, itemConfig.stream().map(ItemConfig::getId).collect(Collectors.toList()));
        //获取用户佩戴的徽章
        User user = userDao.getById(uid);
        return UserAdapter.buildBadge(itemConfig, userBackpacks, user);
    }

    /**
     * @param uid    用户id
     * @param itemId 徽章id
     */
    @Override
    public void badge(Long uid, Long itemId) {
        //确保用户拥有该徽章
        UserBackpack firstValidItem = userBackPackDao.getFirstValidItem(uid, itemId);
        AssertUtil.isNotEmpty(firstValidItem, "你还没有这个徽章，快去获得吧");
        //确保这个物品是徽章
        ItemConfig itemConfig = itemConfigDao.getById(firstValidItem.getItemId());
        AssertUtil.equal(itemConfig.getType(), ItemTypeEnum.BADGE.getType(), "只有徽章才能佩戴");
        userDao.wearingBadge(uid, itemId);
        //删除并更新用户缓存
        userCache.userInfoChange(uid);
    }

    @Override
    @Transactional
    public void black(Long uid) {
        Black user = new Black();
        user.setType(BlackTypeEnum.UID.getType());
        user.setTarget(uid.toString());
        blackDao.save(user);
        User byId = userDao.getById(uid);
        IpInfo info = JsonUtils.toObj(byId.getIpInfo(), IpInfo.class);
        blackIp(Optional.ofNullable(info).map(IpInfo::getCreateIp).orElse(null));
        blackIp(Optional.ofNullable(info).map(IpInfo::getUpdateIp).orElse(null));
        applicationEventPublisher.publishEvent(new UserBlackEvent(this, byId));
    }

    /**
     * 获取用户汇总信息
     *
     * @param req
     * @return
     */
    @Override
    public List<SummeryInfoDTO> getSummeryUserInfo(SummeryInfoReq req) {
        //前端需要更新信息的用户
        List<Long> uidList = getNeedSyncUidList(req.getReqList());
        //获取前端用户需要更新的字段
        Map<Long, SummeryInfoDTO> batch = userSummaryCache.getBatch(uidList);
        return req.getReqList().stream()
                .map(a -> batch.containsKey(a.getUid()) ? batch.get(a.getUid()) : SummeryInfoDTO.skip(a.getUid()))
                .collect(Collectors.toList());
    }

    private List<Long> getNeedSyncUidList(List<SummeryInfoReq.infoReq> reqList) {
        List<Long> NeedSyncUidList = new ArrayList<>();
        List<Long> lastInfoTimes = userCache.getUserModifyTime(reqList.stream().map(SummeryInfoReq.infoReq::getUid).collect(Collectors.toList()));
        for (int i = 0; i < reqList.size(); i++) {
            SummeryInfoReq.infoReq infoReq = reqList.get(i);
            Long modifyTime = lastInfoTimes.get(i);
            if (Objects.isNull(infoReq.getLastModifyTime()) || (Objects.nonNull(modifyTime) && modifyTime > infoReq.getLastModifyTime())) {
                NeedSyncUidList.add(infoReq.getUid());
            }
        }
        return NeedSyncUidList;
    }


    @Override
    public List<ItemInfoDTO> getItemInfo(ItemInfoReq req) {
        return req.getReqList().stream()
                .map(a->{
                    ItemConfig itemConfig = itemCache.getById(a.getItemId());
                    if (Objects.nonNull(a.getLastModifyTime()) && a.getLastModifyTime()>itemConfig.getUpdateTime().getTime()) {
                        return ItemInfoDTO.skip(a.getItemId());
                    }
                    ItemInfoDTO dto = new ItemInfoDTO();
                    dto.setItemId(itemConfig.getId());
                    dto.setImg(itemConfig.getImg());
                    dto.setDescribe(itemConfig.getDescribe());
                    return dto;
                }).collect(Collectors.toList());
    }

    private void blackIp(String ip) {
        if (StringUtils.isBlank(ip)) {
            return;
        }
        try {
            Black insert = new Black();
            insert.setType(BlackTypeEnum.IP.getType());
            insert.setTarget(ip);
            blackDao.save(insert);
        } catch (Exception e) {

        }
    }
}




